#include "GraphEditor.h"
#include <QtGui>
#include "GraphScene.h"
#include "GraphicsItems/ClassItem.h"
#include "GraphicsItems/OperationItem.h"
#include "GraphicsItems/Arrow.h"
#include "GVGraph.h"
#include "StringTools.h"

GraphEditor::GraphEditor(bool readOnly)
{
	_readOnly = readOnly;

	createToolbars();
	createView();

	QVBoxLayout *layout = new QVBoxLayout;
	layout->setContentsMargins(0, 0, 0, 0);
	layout->addWidget(_paletteToolbar);
	layout->addWidget(_view);
	setLayout(layout);
}

GraphEditor::~GraphEditor(void)
{
}

void GraphEditor::paintEvent (QPaintEvent *e)
{
}

void GraphEditor::clear()
{
	_scene->clear();
}

void GraphEditor::createToolbars()
{
	_paletteToolbar = new QToolBar(tr("Palette"));

	QButtonGroup* modeButtons = new QButtonGroup;
	connect(modeButtons, SIGNAL(buttonClicked(int)), this, SLOT(goToMode(int)));

	moveButton = new QToolButton;
	moveButton->setIcon(QIcon(":/images/move.png"));
	moveButton->setText(tr("Move"));
	moveButton->setShortcut(tr("Ctrl+1"));
	moveButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
	moveButton->setCheckable(true);
	moveButton->setChecked(true);
	_paletteToolbar->addWidget(moveButton);
	modeButtons->addButton(moveButton, int(GraphScene::Move));

	addClassButton = new QToolButton;
	addClassButton->setIcon(QIcon(":/images/class.png"));
	addClassButton->setText(tr("Add Class"));
	addClassButton->setShortcut(tr("Ctrl+2"));
	addClassButton->setCheckable(true);
	addClassButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
	_paletteToolbar->addWidget(addClassButton);
	modeButtons->addButton(addClassButton, int(GraphScene::AddClass));

	addOperationButton = new QToolButton;
	addOperationButton->setIcon(QIcon(":/images/operation.png"));
	addOperationButton->setText(tr("Add Operation"));
	addOperationButton->setShortcut(tr("Ctrl+3"));
	addOperationButton->setCheckable(true);
	addOperationButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
	_paletteToolbar->addWidget(addOperationButton);
	modeButtons->addButton(addOperationButton, int(GraphScene::AddOperation));

	addRelationButton = new QToolButton;
	addRelationButton->setIcon(QIcon(":/images/relation.png"));
	addRelationButton->setText(tr("Add Relation"));
	addRelationButton->setShortcut(tr("Ctrl+4"));
	addRelationButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
	addRelationButton->setCheckable(true);
	_paletteToolbar->addWidget(addRelationButton);
	modeButtons->addButton(addRelationButton, int(GraphScene::AddRelation));	
}

void GraphEditor::createView()
{
	_scene = new GraphScene();
	_view = new QGraphicsView(_scene);
	switchMode(_readOnly ? GraphScene::ReadOnly : GraphScene::Move);
}

void GraphEditor::goToMode(int modeIndex)
{
	switchMode((GraphScene::Mode)modeIndex);
}

void GraphEditor::switchMode(GraphScene::Mode mode)
{
	//auto mode = (GraphScene::Mode)modeId;
	
	_readOnly = mode == GraphScene::ReadOnly;

	moveButton->setEnabled(!_readOnly);
	addClassButton->setEnabled(!_readOnly);
	addOperationButton->setEnabled(!_readOnly);
	addRelationButton->setEnabled(!_readOnly);

	_scene->setMode(mode);
}

void GraphEditor::setModel(const SemanticModel::Model& m)
{
	clear();

	QMap<QString, GraphNodeItem*> items;
	GVGraph gv("");
	for (SemanticModel::Model::Nodes i = m.nodes(); i.first != i.second; ++i.first)
	{		
		GraphNodeItem* item;
		switch (i.first.type())
		{
		
		case SemanticModel::OperationNode:
			item = new OperationItem(_scene);
			break;
		case SemanticModel::EssenseNode:
		default:
			item = new ClassItem(_scene);
			break;
		}
		
		QString name = toQString(i.first.name());
		item->setPlainText(qPrintable(name));
		_scene->addItem(item);
		items[name] = item;

		QRectF rect = item->boundingRect();
		gv.addNode(qPrintable(name), rect.width(), rect.height());
	}

	QList<Arrow*> arrows;
	for (SemanticModel::Model::RelationsAll i = m.relations(); i.first != i.second; ++i.first)
	{
		SemanticModel::Model::RelationAll& r = i.first;
		QString roleA = toQString(r.roleA());
		QString roleB = toQString(r.roleB());
		gv.addEdge(qPrintable(roleA), qPrintable(roleB));

		SemanticModel::Relation relation = r.relation();

		GraphNodeItem* itemA = items[roleA];
		GraphNodeItem* itemB = items[roleB];
		Arrow* item = new Arrow(_scene, itemA, itemB, relation.type());
		arrows.append(item);
	}

	gv.applyLayout();

	std::vector<GVNode> layout = gv.nodes();
	for (std::vector<GVNode>::const_iterator i = layout.begin(); i != layout.end(); ++i)
	{
		const GVNode& nodeLayout = *i;
		GraphNodeItem* node = items[nodeLayout.name];
		node->setPos(nodeLayout.x, nodeLayout.y);
	}

	foreach(Arrow* arrow, arrows)
		arrow->updatePosition();

	
	_scene->setSceneRect(gv.boundingRect());
}


bool GraphEditor::dirty()
{
	return true;
}

void GraphEditor::setDirty(bool dirty)
{
}

const QString& GraphEditor::fileFilter()
{
	static const QString ext = "gen2 Graph file (*.gen2g)";
	return ext;
}

bool GraphEditor::save(const QString& path)
{  
	return false;
}

bool GraphEditor::load(const QString& path)
{   
	return false;
}


SemanticModel::ModelPtr GraphEditor::generateModel()
{
	const int bufferSize = 200;
	SemanticModel::ModelPtr result(new SemanticModel::Model);
	
	foreach (QGraphicsItem *item, _scene->items()) 
	{
		if (item->type() == ClassItem::Type)
		{
			ClassItem* classItem = (ClassItem*)item;
			wchar_t text[bufferSize];
			toCString(classItem->toPlainText(), text);
			result->addEssense(text);
		}
	}

	foreach (QGraphicsItem *item, _scene->items()) 
	{
		if (item->type() == OperationItem::Type)
		{
			ClassItem* operationItem = (ClassItem*)item;
			wchar_t text[bufferSize];
			toCString(operationItem->toPlainText(), text);
			result->addOperation(text);
		}
	}

	foreach (QGraphicsItem *item, _scene->items()) 
	{
		if (item->type() == Arrow::Type)
		{
			Arrow* r = (Arrow*)item;
			wchar_t roleA[bufferSize], roleB[bufferSize];
			toCString(r->startItem()->toPlainText(), roleA);
			toCString(r->endItem()->toPlainText(), roleB);
			result->addRelation(roleA, r->relation(), roleB);
		}
	}
	
	return result;
}
